package lecho.lib.hellocharts.gesture;

import lecho.lib.hellocharts.view.PieChartView;
import android.content.Context;
import android.graphics.RectF;

import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;

import androidx.core.widget.ScrollerCompat;

/**
 * Touch handler for PieChart. It doesn't handle zoom and scroll like default ChartTouchHandler. Instead it uses
 * Scroller(ScrollerCompat) directly to compute PieChart rotation when user scroll. ChartScroller and ChartZoomer are
 * not really used here.
 * 
 */
public class PieChartTouchHandler extends ChartTouchHandler {
	/**
	 * The initial fling velocity is divided by this amount.
	 */
	public static final int FLING_VELOCITY_DOWNSCALE = 4;

	/**
	 * PieChartTouchHandler uses its own instance of Scroller.
	 */
	protected ScrollerCompat scroller;
	/**
	 * Reference to PieChartView to use some methods specific for that kind of chart.
	 */
	protected PieChartView pieChart;

	private boolean isRotationEnabled = true;

	public PieChartTouchHandler(Context context, PieChartView chart) {
		super(context, chart);
		pieChart = (PieChartView) chart;
		scroller = ScrollerCompat.create(context);
		gestureDetector = new GestureDetector(context, new ChartGestureListener());
		scaleGestureDetector = new ScaleGestureDetector(context, new ChartScaleGestureListener());
		isZoomEnabled = false;// Zoom is not supported by PieChart.
	}

	@Override
	public boolean computeScroll() {
		if (!isRotationEnabled) {
			return false;
		}
		if (scroller.computeScrollOffset()) {
			pieChart.setChartRotation(scroller.getCurrY(), false);
			// pieChart.setChartRotation() will invalidate view so no need to return true;
		}
		return false;
	}

	@Override
	public boolean handleTouchEvent(MotionEvent event) {
		boolean needInvalidate = super.handleTouchEvent(event);

		if (isRotationEnabled) {
			needInvalidate = gestureDetector.onTouchEvent(event) || needInvalidate;
		}
		return needInvalidate;
	}

	public boolean isRotationEnabled() {
		return isRotationEnabled;
	}

	public void setRotationEnabled(boolean isRotationEnabled) {
		this.isRotationEnabled = isRotationEnabled;
	}

	private class ChartScaleGestureListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {

		@Override
		public boolean onScale(ScaleGestureDetector detector) {
			// No scale for PieChart.
			return false;
		}
	}

	private class ChartGestureListener extends GestureDetector.SimpleOnGestureListener {
		@Override
		public boolean onDown(MotionEvent e) {
			if (isRotationEnabled) {
				scroller.abortAnimation();
				return true;
			}

			return false;

		}

		@Override
		public boolean onDoubleTap(MotionEvent e) {
			return false;
		}

		@Override
		public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
			if (isRotationEnabled) {
				// Set the pie rotation directly.
				final RectF circleOval = pieChart.getCircleOval();
				final float centerX = circleOval.centerX();
				final float centerY = circleOval.centerY();
				float scrollTheta = vectorToScalarScroll(distanceX, distanceY, e2.getX() - centerX, e2.getY() - centerY);
				pieChart.setChartRotation(pieChart.getChartRotation() - (int) scrollTheta / FLING_VELOCITY_DOWNSCALE,
						false);
				return true;
			}

			return false;
		}

		@Override
		public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
			if (isRotationEnabled) {
				// Set up the Scroller for a fling
				final RectF circleOval = pieChart.getCircleOval();
				final float centerX = circleOval.centerX();
				final float centerY = circleOval.centerY();
				float scrollTheta = vectorToScalarScroll(velocityX, velocityY, e2.getX() - centerX, e2.getY() - centerY);
				scroller.abortAnimation();
				scroller.fling(0, (int) pieChart.getChartRotation(), 0, (int) scrollTheta / FLING_VELOCITY_DOWNSCALE,
						0, 0, Integer.MIN_VALUE, Integer.MAX_VALUE);
				return true;
			}

			return false;
		}

		/**
		 * Helper method for translating (x,y) scroll vectors into scalar rotation of the pie.
		 * 
		 * @param dx
		 *            The x component of the current scroll vector.
		 * @param dy
		 *            The y component of the current scroll vector.
		 * @param x
		 *            The x position of the current touch, relative to the pie center.
		 * @param y
		 *            The y position of the current touch, relative to the pie center.
		 * @return The scalar representing the change in angular position for this scroll.
		 */
		private float vectorToScalarScroll(float dx, float dy, float x, float y) {
			// get the length of the vector
			float l = (float) Math.sqrt(dx * dx + dy * dy);

			// decide if the scalar should be negative or positive by finding
			// the dot product of the vector perpendicular to (x,y).
			float crossX = -y;
			float crossY = x;

			float dot = (crossX * dx + crossY * dy);
			float sign = Math.signum(dot);

			return l * sign;
		}
	}

}
